/*
 * Licensed to the 南京汇海交通科技有限公司
 *
 * Copyright (c) 2025.
 * All rights reserved.
 *
 * 本软件属南京汇海交通科技有限公司所有，在未获得南京汇海交通科技有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.chframework.boot.core.security;

import cn.hutool.core.util.IdUtil;
import com.chframework.boot.core.pojo.KeyValuePair;
import com.chframework.boot.core.properties.TokenProperty;
import com.chframework.boot.core.properties.TokenProperty.KeyType;
import com.chframework.common.exception.SystemInterrupterException;
import com.chframework.boot.core.response.status.ExceptionStatusEnum;
import com.chframework.common.constant.ApplicationConstant;
import com.chframework.common.exception.ApplicationException;
import com.chframework.common.extend.StringHelper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtParserBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class TokenHandler {
//CHECKSTYLE:OFF

    private static TokenProperty tokenProperty;

    public TokenHandler(TokenProperty tokenProperty) {
        TokenHandler.tokenProperty = tokenProperty;
    }

    /**
     * 创建token
     */
    public static String createToken(String json) throws ApplicationException {
        Claims claims = Jwts.claims();
        claims.put(ApplicationConstant.DEFAULT_JSON_KEY, json);
        return createToken(claims);
    }

    /**
     * 创建token
     */
    public static String createToken(List<KeyValuePair> properties) throws ApplicationException {
        LinkedHashMap<String, Object> data = properties.parallelStream()
                .collect(Collectors.toMap(
                        KeyValuePair::getKey,
                        KeyValuePair::getValue,
                        (oldValue, newValue) -> newValue,
                        LinkedHashMap::new)
                );
        return createToken(Jwts.claims(data));
    }

    /**
     * 创建token
     */
    public static String createToken(Map<String, Object> data) throws ApplicationException {
        return createToken(Jwts.claims(data));
    }

    /**
     * 创建token
     */
    public static String createToken(Claims claims) throws ApplicationException {
        try {
            // 验证配置合法性
            validate(claims);
            // 开始准备token构造器
            JwtBuilder builder = Jwts.builder().setClaims(claims);

            if (StringUtils.isNotBlank(tokenProperty.getIssuer())) {
                builder.setIssuer(tokenProperty.getIssuer());
            }
            if (StringUtils.isNotBlank(tokenProperty.getSubject())) {
                builder.setSubject(tokenProperty.getSubject());
            }
            if (StringUtils.isNotBlank(tokenProperty.getAudience())) {
                builder.setAudience(tokenProperty.getAudience());
            }

            Date date = new Date();
            if (tokenProperty.getExpire() == null || tokenProperty.getExpire() <= 0) {
                builder.setExpiration(new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(24)));
            } else {
                builder.setExpiration(new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(tokenProperty.getExpire())));
            }
            builder.setNotBefore(date);
            builder.setIssuedAt(date);
            builder.setId(IdUtil.objectId());

            if (tokenProperty.getKeyType() == KeyType.ASYMMETRIC) {
                // 非对称签名算法 使用【私钥】 创建签名(token)
                PrivateKey privateKey = getPrivateKey(tokenProperty.getAlgorithm().getFamilyName(), tokenProperty.getPrivateKey());
                builder.signWith(privateKey, tokenProperty.getAlgorithm());
            } else if (tokenProperty.getKeyType() == KeyType.SYMMETRICAL) {
                // 对称签名算法 直接使用【秘钥】 创建签名(token)
                SecretKey secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(tokenProperty.getSecretKey()));
                builder.signWith(secretKey, tokenProperty.getAlgorithm());
            } else if (tokenProperty.getKeyType() == KeyType.NONE) {
                // 使用【明文密码】 创建签名(token)
                SecretKey plainKey = Keys.hmacShaKeyFor(tokenProperty.getPlainKey().getBytes(StandardCharsets.UTF_8));
                builder.signWith(plainKey);
            } else {
                throw new SystemInterrupterException(ExceptionStatusEnum.NEBULOUS_SECRET_KEY_TYPE);
            }

            // 生成token并返回
            String token = builder.compact();
            return String.join(StringUtils.EMPTY, tokenProperty.getPrefix(), token);
        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
            throw new ApplicationException(e);
        }
    }

    /**
     * 解析token
     */
    public static Claims parse(String token) throws ApplicationException {
        try {
            JwtParserBuilder builder = Jwts.parserBuilder();

            if (StringUtils.isNotBlank(tokenProperty.getSubject())) {
                builder.requireSubject(tokenProperty.getSubject());
            }

            if (tokenProperty.getKeyType() == KeyType.ASYMMETRIC) {
                // 非对称签名算法 使用【公钥】验证与获取token数据
                PublicKey publicKey = getPublicKey(tokenProperty.getAlgorithm().getFamilyName(), tokenProperty.getPublicKey());
                builder.setSigningKey(publicKey);
            } else if (tokenProperty.getKeyType() == KeyType.SYMMETRICAL) {
                // 对称签名算法 直接使用【秘钥】验证与获取token数据
                builder.setSigningKey(Keys.hmacShaKeyFor(Decoders.BASE64.decode(tokenProperty.getSecretKey())));
            } else if (tokenProperty.getKeyType() == KeyType.NONE) {
                // 使用【明文密码】 验证与获取token数据
                builder.setSigningKey(tokenProperty.getPlainKey().getBytes(StandardCharsets.UTF_8));
            } else {
                throw new SystemInterrupterException(ExceptionStatusEnum.NEBULOUS_SECRET_KEY_TYPE);
            }

            if (!(tokenProperty.getSkewSeconds() == null || tokenProperty.getSkewSeconds() < 0)) {
                builder.setAllowedClockSkewSeconds(tokenProperty.getSkewSeconds());
            }

            return builder.build()
                    .parseClaimsJws(StringHelper.trimStart(token, tokenProperty.getPrefix()))
                    .getBody();
        } catch (Exception e) {
            throw new ApplicationException(e);
        }
    }

    /**
     * 创建秘钥
     *
     * @param algorithm 支持对称签名的算法
     */
    public static String createSecretKey(SignatureAlgorithm algorithm) {
        if (!Arrays.asList(SignatureAlgorithm.HS256, SignatureAlgorithm.HS384, SignatureAlgorithm.HS512)
                .contains(algorithm)) {
            throw new SystemInterrupterException(ExceptionStatusEnum.UNSUPPORTED_ALGORITHM_SYMMETRICAL);
        }
        SecretKey secretKey = Keys.secretKeyFor(algorithm);
        return Encoders.BASE64.encode(secretKey.getEncoded());
    }

    /**
     * 校验token的配置信息
     */
    private static void validate(Claims claims) throws ApplicationException {
        try {
            // token数据 不允许设置为空
            if (claims.size() <= 0) {
                throw new SystemInterrupterException(ExceptionStatusEnum.TOKEN_DATA_REQUIRED_NON_NULL);
            }
            // 非对称签名算法 要求【公钥】和【私钥】非空
            if (tokenProperty.getKeyType() == KeyType.ASYMMETRIC && StringUtils.isAnyBlank(tokenProperty.getPrivateKey(), tokenProperty.getPublicKey())) {
                throw new SystemInterrupterException(ExceptionStatusEnum.ILLEGAL_PROPERTIES_ASYMMETRIC);
            }
            // 对称签名算法 要求【秘钥】非空
            if (tokenProperty.getKeyType() == KeyType.SYMMETRICAL && StringUtils.isBlank(tokenProperty.getSecretKey())) {
                throw new SystemInterrupterException(ExceptionStatusEnum.ILLEGAL_PROPERTIES_SYMMETRICAL);
            }
            // 不使用签名算法 要求【密码字符串】非空
            if (tokenProperty.getKeyType() == KeyType.NONE && StringUtils.isBlank(tokenProperty.getPlainKey())) {
                throw new SystemInterrupterException(ExceptionStatusEnum.ILLEGAL_PROPERTIES_PLAINTEXT);
            }
            // 若使用【秘钥】或者【公钥】和【私钥】验证token，签名算法不允许为空
            if ((tokenProperty.getKeyType() == KeyType.ASYMMETRIC || tokenProperty.getKeyType() == KeyType.SYMMETRICAL)
                    && tokenProperty.getAlgorithm() == null) {
                throw new SystemInterrupterException(ExceptionStatusEnum.ILLEGAL_PROPERTIES_ALGORITHM);
            }
        } catch (Exception e) {
            throw new ApplicationException(e);
        }
    }

    /**
     * 获取私钥实例
     */
    private static PrivateKey getPrivateKey(String algorithm, String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] privateKeyBytes = Decoders.BASE64.decode(privateKey);
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        return keyFactory.generatePrivate(pkcs8EncodedKeySpec);
    }

    /**
     * 获取公钥实例
     */
    private static PublicKey getPublicKey(String algorithm, String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] publicKeyBytes = Decoders.BASE64.decode(publicKey);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
        return keyFactory.generatePublic(x509EncodedKeySpec);
    }

//CHECKSTYLE:ON
}
